Skip to content

46129 frontend [ templates ] Add FieldSets#205

Open
Maria-Lordwill wants to merge 118 commits into
masterfrom
frontend/templates/46129__add_fieldsets
Open

46129 frontend [ templates ] Add FieldSets#205
Maria-Lordwill wants to merge 118 commits into
masterfrom
frontend/templates/46129__add_fieldsets

Conversation

@Maria-Lordwill
Copy link
Copy Markdown
Collaborator

@Maria-Lordwill Maria-Lordwill commented Apr 24, 2026

Note

High Risk
Large schema migration plus workflow/template/condition and task-completion paths; misconfiguration of SERVER_ADDRESS or predicate migration could break hosts or start-task logic.

Overview
This PR adds reusable fieldsets on templates and workflows: grouped fields with layout/label options, M2M links to kickoff and tasks, and sum_equal rules validated at kickoff update and task completion. Template and workflow APIs now expose fieldsets alongside flat fields; output-field resolution includes fields inside linked fieldsets.

Task conditions gain skipped and completed_or_skipped operators (with a data migration rewriting existing task completed predicates to completed_or_skipped). start_task conditions may use any of the three completion-style operators.

Supporting changes: dedicated FieldSetTemplateService / workflow FieldSetService, RelatedApiNameField for slug lookups, default delete() on BaseModelService, and template versioning schemas extended for fieldsets.

Ops/docs: root README uses SERVER_ADDRESS instead of FRONTEND_DOMAIN / BACKEND_DOMAIN; Celery compose services mirror more optional integration env vars and use SERVER_ADDRESS in ALLOWED_HOSTS.

Reviewed by Cursor Bugbot for commit 16adbfe. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add FieldSets to templates and workflows for grouping task and kickoff fields

  • Introduces FieldsetTemplate, FieldSet, and related rule/link models to group fields on template kickoffs, template tasks, workflow kickoffs, and workflow tasks with layout, label position, and validation rules (e.g. sum_equal).
  • Adds backend REST endpoints (GET/POST /templates/{id}/fieldsets, GET/PATCH/DELETE /templates/fieldsets/{id}) via FieldsetTemplateViewSet and a dedicated FieldSetTemplateService with transactional create/update/delete of nested fields and rules.
  • Extends template and workflow serializers, task/kickoff version services, and get_kickoff_fields/get_task_output_fields to include fieldset-associated fields alongside direct fields.
  • Adds two new predicate operators (SKIPPED, COMPLETED_OR_SKIPPED) for task conditions, updates get_tasks_parents and TaskResolver to handle them, and exposes them in the frontend condition UI.
  • Adds a frontend Fieldsets management section (/templates/:id/fieldsets/) with list, detail, create, and delete views (FieldsetCard, FieldsetDetails, FieldsetModal), Redux slice, sagas, and selectors.
  • Replaces per-field ExtraFieldIntl rendering in TaskCard, WorkflowEditPopup, KickoffRedux, and KickoffEdit with a unified MergedOutputList/OutputFormTaskMerged that interleaves fields and fieldsets ordered by order.
  • Updates TuneViewModal, FeedItemHeader, KickoffOutputs, and WorkflowLogTaskComplete to display and count fieldset-grouped fields.
  • Updates the interactive start.sh to prompt for SERVER_ADDRESS, generate secrets, and use default.env; nginx configuration is refactored into reusable includes under nginx/includes/.
  • Risk: The storageOutputs localStorage format changed (output key → data), so any existing browser-stored task drafts will not be restored after this release.

Macroscope summarized 16adbfe.

@Maria-Lordwill Maria-Lordwill added bug Something isn't working Frontend Web client changes request labels Apr 24, 2026
Comment thread backend/src/processes/services/tasks/task_version.py
Comment thread backend/src/processes/services/templates/fieldsets/fieldset_rule.py
Comment thread backend/src/processes/services/workflows/fieldsets/fieldset_rule.py
Comment thread backend/src/processes/models/templates/fieldset.py
Comment thread backend/src/processes/serializers/templates/task.py Outdated
Comment thread backend/src/processes/services/workflows/fieldsets/fieldset.py
for field in self.instance.fields.all():
if field.value not in self.NULL_VALUES:
total += float(field.value)
if total != float(self.instance.value):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium fieldsets/fieldset_rule.py:22

Floating-point comparison total != float(self.instance.value) incorrectly rejects valid field sets due to precision loss. For example, when fields sum to 0.3 via 0.1 + 0.2, the strict equality check fails because 0.1 + 0.2 != 0.3 in IEEE 754 arithmetic. Consider using a tolerance-based comparison like abs(total - float(self.instance.value)) > epsilon.

-        if total != float(self.instance.value):
+        if abs(total - float(self.instance.value)) > 1e-9:
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file backend/src/processes/services/workflows/fieldsets/fieldset_rule.py around line 22:

Floating-point comparison `total != float(self.instance.value)` incorrectly rejects valid field sets due to precision loss. For example, when fields sum to `0.3` via `0.1 + 0.2`, the strict equality check fails because `0.1 + 0.2 != 0.3` in IEEE 754 arithmetic. Consider using a tolerance-based comparison like `abs(total - float(self.instance.value)) > epsilon`.

Evidence trail:
backend/src/processes/services/workflows/fieldsets/fieldset_rule.py lines 16-24 at REVIEWED_COMMIT - shows the `_validate_sum_equal` method with `total != float(self.instance.value)` comparison on line 22. IEEE 754 floating-point precision issues are well-documented (e.g., Python docs on floating point: https://docs.python.org/3/tutorial/floatingpoint.html).

Comment on lines +19 to +21
def _validate(self, **kwargs):
field_type = kwargs.get('type')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low templates/field_template.py:19

In FieldTemplateService.partial_update, _validate checks field_type == FieldType.USER against kwargs.get('type'), but during partial updates type may not be provided. When updating only is_required=False on an existing USER field, field_type becomes None and the validation incorrectly passes, allowing a USER field to become non-required. Consider using self.instance.type as a fallback when type is not in update_kwargs.

    def _validate(self, **kwargs):
-        field_type = kwargs.get('type')
+        field_type = kwargs.get('type', self.instance.type if self.instance else None)
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file backend/src/processes/services/templates/field_template.py around lines 19-21:

In `FieldTemplateService.partial_update`, `_validate` checks `field_type == FieldType.USER` against `kwargs.get('type')`, but during partial updates `type` may not be provided. When updating only `is_required=False` on an existing `USER` field, `field_type` becomes `None` and the validation incorrectly passes, allowing a `USER` field to become non-required. Consider using `self.instance.type` as a fallback when `type` is not in `update_kwargs`.

Evidence trail:
backend/src/processes/services/templates/field_template.py lines 18-27 (_validate method), lines 33-40 (partial_update method), backend/src/generics/base/service.py lines 13-30 (BaseModelService.__init__ showing self.instance is set from constructor)

Comment on lines +37 to +42
def _validate(self, **kwargs):

""" Call after objects save """

validator = getattr(self, f'_validate_{self.instance.type}', None)
validator(**kwargs)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low fieldsets/fieldset_rule.py:37

In _validate, validator is fetched with getattr(..., None), but validator(**kwargs) is called unconditionally. When self.instance.type has no matching _validate_{type} method, this raises TypeError: 'NoneType' object is not callable instead of a clear validation error.

    def _validate(self, **kwargs):

        """ Call after objects save """

        validator = getattr(self, f'_validate_{self.instance.type}', None)
-        validator(**kwargs)
+        if validator is not None:
+            validator(**kwargs)
Also found in 1 other location(s)

backend/src/processes/services/templates/fieldsets/fieldset.py:113

In _validate_rules, calling service._validate() will crash with TypeError: 'NoneType' object is not callable for any rule whose type does not have a corresponding _validate_{type} method. The referenced FieldsetTemplateRuleService._validate() method uses getattr(self, f'_validate_{self.instance.type}', None) and then calls the result without checking if it's None. Only _validate_sum_equal exists in the visible code, so any rule with a different type will fail.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file backend/src/processes/services/templates/fieldsets/fieldset_rule.py around lines 37-42:

In `_validate`, `validator` is fetched with `getattr(..., None)`, but `validator(**kwargs)` is called unconditionally. When `self.instance.type` has no matching `_validate_{type}` method, this raises `TypeError: 'NoneType' object is not callable` instead of a clear validation error.

Evidence trail:
backend/src/processes/services/templates/fieldsets/fieldset_rule.py lines 37-38 at REVIEWED_COMMIT (getattr with None default, unconditional call)
backend/src/processes/enums.py lines 747-756 at REVIEWED_COMMIT (FieldSetRuleType only defines 'sum_equal')
backend/src/processes/models/mixins.py lines 349-359 at REVIEWED_COMMIT (BaseFieldSetRuleMixin defines type field with choices=FieldSetRuleType.CHOICES - application-level validation only)

Also found in 1 other location(s):
- backend/src/processes/services/templates/fieldsets/fieldset.py:113 -- In `_validate_rules`, calling `service._validate()` will crash with `TypeError: 'NoneType' object is not callable` for any rule whose `type` does not have a corresponding `_validate_{type}` method. The referenced `FieldsetTemplateRuleService._validate()` method uses `getattr(self, f'_validate_{self.instance.type}', None)` and then calls the result without checking if it's `None`. Only `_validate_sum_equal` exists in the visible code, so any rule with a different type will fail.

Comment thread frontend/src/public/components/KickoffEdit/KickoffEdit.tsx
}
"""

if data.get('fields'):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low workflows/kickoff_version.py:180

When data contains {'fields': [], 'fieldsets': []}, the empty fields list is skipped due to truthiness check on line 180, but empty fieldsets is processed via is not None check on line 182. This causes _update_fieldsets to delete all fieldsets while _update_fields leaves orphaned fields intact — inconsistent behavior for empty list inputs. Consider changing line 180 to if data.get('fields') is not None: to match the fieldsets handling.

Also found in 1 other location(s)

backend/src/processes/services/tasks/task_version.py:66

If field_data.get('selections') returns an empty list [], the condition on line 66 evaluates to falsy, so the method exits without deleting existing selections. This means when a field previously had selections but the new data has 'selections': [], the old selections will incorrectly remain in the database instead of being cleared.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file backend/src/processes/services/workflows/kickoff_version.py around line 180:

When `data` contains `{'fields': [], 'fieldsets': []}`, the empty `fields` list is skipped due to truthiness check on line 180, but empty `fieldsets` is processed via `is not None` check on line 182. This causes `_update_fieldsets` to delete all fieldsets while `_update_fields` leaves orphaned fields intact — inconsistent behavior for empty list inputs. Consider changing line 180 to `if data.get('fields') is not None:` to match the fieldsets handling.

Evidence trail:
backend/src/processes/services/workflows/kickoff_version.py lines 180-183 (condition checks), lines 63-77 (_update_fields with delete at line 76-78), lines 136-163 (_update_fieldsets with delete at lines 161-163). Verified at REVIEWED_COMMIT.

Also found in 1 other location(s):
- backend/src/processes/services/tasks/task_version.py:66 -- If `field_data.get('selections')` returns an empty list `[]`, the condition on line 66 evaluates to falsy, so the method exits without deleting existing selections. This means when a field previously had selections but the new data has `'selections': []`, the old selections will incorrectly remain in the database instead of being cleared.

Comment thread frontend/src/public/components/TemplateEdit/FieldsetPicker/FieldsetPicker.tsx Outdated
Comment thread backend/src/processes/services/templates/fieldsets/fieldset.py
Comment thread backend/src/processes/services/workflows/fieldsets/fieldset_rule.py
Comment thread backend/src/processes/models/templates/fieldset.py
Comment thread backend/src/processes/serializers/workflows/kickoff_value.py
Comment thread backend/src/processes/models/workflows/workflow.py
Comment thread backend/src/processes/services/workflow_action.py
Comment thread backend/src/processes/models/templates/fieldset.py
selection_ids.add(selection.id)
field.selections.exclude(id__in=selection_ids).delete()
self._update_field_selections(field, field_data)
self.instance.output.exclude(id__in=field_ids).delete()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High tasks/task_version.py:94

In _update_fields, the deletion at line 94 removes ALL TaskField objects with task=self.instance, including fields that belong to fieldsets. Since _update_fields runs before _update_fieldsets (lines 531-532), and field_ids only tracks non-fieldset fields, existing fieldset fields are deleted before _update_fieldsets can recreate them. The deletion should filter to only remove fields without a fieldset using self.instance.output.filter(fieldset__isnull=True).exclude(id__in=field_ids).delete().

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file backend/src/processes/services/tasks/task_version.py around line 94:

In `_update_fields`, the deletion at line 94 removes ALL `TaskField` objects with `task=self.instance`, including fields that belong to fieldsets. Since `_update_fields` runs before `_update_fieldsets` (lines 531-532), and `field_ids` only tracks non-fieldset fields, existing fieldset fields are deleted before `_update_fieldsets` can recreate them. The deletion should filter to only remove fields without a fieldset using `self.instance.output.filter(fieldset__isnull=True).exclude(id__in=field_ids).delete()`.

Evidence trail:
backend/src/processes/services/tasks/task_version.py lines 82-94 (_update_fields: field_ids only collects non-fieldset fields, then deletes all output not in field_ids), lines 91 (fieldset=None), line 94 (self.instance.output.exclude(id__in=field_ids).delete()), lines 531-532 (_update_fields called before _update_fieldsets), lines 167-187 (_update_field uses TaskField.objects.update_or_create with fieldset in lookup), lines 231-244 (_update_fieldset_fields), lines 246-276 (_update_fieldsets). backend/src/processes/models/workflows/fields.py lines 47-53 (TaskField.task FK with related_name='output'), lines 60-65 (TaskField.fieldset FK nullable), line 76 (objects = BaseSoftDeleteManager). backend/src/generics/managers.py lines 6-8 (BaseSoftDeleteManager filters is_deleted=False). backend/src/generics/querysets.py lines 59-61 (BaseQuerySet.delete does soft delete via update is_deleted=True).

Comment thread backend/src/processes/services/templates/fieldsets/fieldset.py
Comment on lines +246 to +254
def _update_fieldsets(self, data: Optional[List]) -> None:

fieldset_api_names = set()
for fieldset_data in data or []:
task_link = next(
link for link in fieldset_data['task_links']
if link['task_api_name'] == self.instance.api_name
)
order = task_link['order']
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium tasks/task_version.py:246

In _update_fieldsets, the next() call at line 250 raises StopIteration if no task_link matches self.instance.api_name, causing the entire update operation to fail. Consider providing a default value with None and handling the missing case gracefully.

        fieldset_api_names = set()
         for fieldset_data in data or []:
-            task_link = next(
-                link for link in fieldset_data['task_links']
-                if link['task_api_name'] == self.instance.api_name
-            )
-            order = task_link['order']
+            task_link = next(
+                (link for link in fieldset_data['task_links']
+                 if link['task_api_name'] == self.instance.api_name),
+                None
+            )
+            if task_link is None:
+                continue
+            order = task_link['order']
             fieldset, _ = FieldSet.objects.update_or_create(
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file backend/src/processes/services/tasks/task_version.py around lines 246-254:

In `_update_fieldsets`, the `next()` call at line 250 raises `StopIteration` if no `task_link` matches `self.instance.api_name`, causing the entire update operation to fail. Consider providing a default value with `None` and handling the missing case gracefully.

Evidence trail:
backend/src/processes/services/tasks/task_version.py lines 246-253 (the `next()` call without default), lines 498-532 (`update_from_version` calling `_update_fieldsets`), backend/src/processes/services/versioning/schemas.py lines 84-93 (FieldsetTemplateTaskTemplateSchemaV1), lines 100-135 (FieldSetSchemaV1 with task_links), lines 266-295 (TaskSchemaV1 with fieldsets), backend/src/processes/services/workflows/workflow_version.py lines 61-80 (_update_tasks_from_version passing data).

service.validate_rules()
except FieldsetServiceException as ex:
self.raise_validation_error(message=ex.message)
for field_template in kickoff.fields.filter(fieldset__isnull=True):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low workflows/kickoff_value.py:113

When creating fields without a fieldset at lines 113-120, fieldset_id is not passed to TaskFieldService.create(). If the field template has rules, _link_rules accesses kwargs['fieldset_id'] with direct key access and raises KeyError, which escapes the except (TaskFieldException, FieldsetServiceException) block at line 121. Pass fieldset_id=None for fields without a fieldset.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file backend/src/processes/serializers/workflows/kickoff_value.py around line 113:

When creating fields without a fieldset at lines 113-120, `fieldset_id` is not passed to `TaskFieldService.create()`. If the field template has rules, `_link_rules` accesses `kwargs['fieldset_id']` with direct key access and raises `KeyError`, which escapes the `except (TaskFieldException, FieldsetServiceException)` block at line 121. Pass `fieldset_id=None` for fields without a fieldset.

Evidence trail:
backend/src/processes/serializers/workflows/kickoff_value.py lines 113-121 (REVIEWED_COMMIT): `service.create()` called without `fieldset_id`; except block catches only `TaskFieldException, FieldsetServiceException`.
backend/src/generics/base/service.py lines 65-70: `BaseModelService.create(**kwargs)` passes kwargs to `_create_related(**kwargs)`.
backend/src/processes/services/tasks/field.py line 326: `_create_related` calls `_link_rules(instance_template, **kwargs)` if rules exist.
backend/src/processes/services/tasks/field.py line 375: `_link_rules` uses `kwargs['fieldset_id']` (direct key access, raises KeyError if missing).
backend/src/processes/models/templates/fields.py lines 59-68: `FieldTemplate.fieldset` is nullable, and `rules` M2M has no constraint preventing association on fieldset-less fields.

Comment thread backend/src/processes/models/templates/fieldset.py Outdated
if field.value not in self.NULL_VALUES:
total += float(field.value)
if total != float(self.instance.value):
raise FieldsetServiceException(MSG_FS_0002(self.instance.value))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Floating-point equality check in sum validation is unreliable

Medium Severity

_validate_sum_equal accumulates field values using float() addition and then compares the total with exact equality (!=). Floating-point arithmetic can produce rounding errors (e.g., 0.1 + 0.2 != 0.3), causing valid inputs to fail validation spuriously. Using Decimal or an epsilon-based comparison would be more reliable.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5944362. Configure here.

Comment thread frontend/src/public/components/TemplateEdit/FieldsetPicker/FieldsetPicker.tsx Outdated
Comment thread frontend/src/public/components/Fieldsets/FieldsetDetails/FieldsetDetails.tsx Outdated
…sorted list across workflow log, task log and kickoff of workflow modal
Comment thread frontend/src/public/components/KickoffOutputs/KickoffOutputs.tsx Outdated
from django.db import models # noqa : PLC0415
if isinstance(instance, models.Manager):
instance = instance.first()
return super().to_representation(instance)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KickoffListSerializer crashes when no kickoff exists

Medium Severity

KickoffListSerializer.to_representation handles Manager instances by calling .first() but doesn't check for None afterward. If no kickoff exists, super().to_representation(None) will crash. The sibling KickoffSerializer correctly handles this case by returning {'fields': [], 'fieldsets': []} when instance is None, but KickoffListSerializer omits that guard.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 15db8dd. Configure here.

order=kwargs['order'],
label_position=instance_template.label_position,
layout=instance_template.layout,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fieldset _create_instance missing account_id assignment

Medium Severity

FieldSetService._create_instance passes account=self.account when creating a FieldSet, but the service receives account_id as a kwarg (not a user-based account). When called from create_fieldsets_from_template or KickoffValueSerializer, self.account may be None because BaseModelService.__init__ only sets self.account from user.account — yet the service is often instantiated with only user, while account_id is passed separately via kwargs but never used in _create_instance.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5588c69. Configure here.

pneumojoseph and others added 2 commits May 22, 2026 00:45
1. Add deafault.env
2. Add SSL options to the install.sh
3. Add all environment variables to the docker compose files
is_superuser=self.is_superuser,
auth_type=self.auth_type,
)
service.validate_rules()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fieldset rule violations swallowed as uncaught exception during task completion

High Severity

service.validate_rules() is called inside complete_task with no surrounding try/except. FieldSetService.validate_rules() raises FieldsetServiceException when a rule (e.g. SUM_EQUAL) is violated. Without a handler, the exception propagates uncaught through the transaction.atomic() block, rolling back the transaction and surfacing as a 500 error instead of a proper validation response. The parallel path in KickoffValueSerializer.update correctly wraps the same call in try/except FieldsetServiceException.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3e05694. Configure here.


fs_api_names = set()
for fs_data in data or []:
order = fs_data['kickoff_links'][0]['order']
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty kickoff links crashes version sync

Medium Severity

_update_fieldsets sets order from fs_data['kickoff_links'][0]['order'] without checking that kickoff_links is non-empty. Version payload with a fieldset and no kickoff link entries causes an IndexError during workflow template updates.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5e2e65a. Configure here.

pass
fieldsets_api_names.append(elem['fieldset']['api_name'])
except (TypeError, ValueError):
continue
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Embed draft misses fieldset fields

Medium Severity

_get_raw_fields_from_kickoff reads kickoff fieldset links from fieldsettemplatekickoff_set and expects each entry to nest the api name under fieldset, while draft normalization stores fieldsets with top-level api_name. Required-field metadata for embed/public flows can omit fieldset member fields.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5e2e65a. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

There are 11 total unresolved issues (including 8 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cff7011. Configure here.

'order': link['order'],
}
for link in fieldsets_raw
]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kickoff fieldset links not saved

High Severity

Template create/update reads kickoff and task fieldset links from fieldsettemplatekickoff_set / fieldsettemplatetasktemplate_set, but DRF validated payloads use the serializer field name fieldsets. The pop always yields nothing, so create_or_update_kickoff_links and per-task tasks_fieldsets never run and fieldset associations on kickoff/tasks are not persisted when saving a template.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cff7011. Configure here.

'api_name': link['fieldset']['api_name'],
'order': link['order'],
}
for link in fieldsets_links_raw
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong api_name in link parsing

High Severity

When kickoff/task fieldset links are present, the code reads link['fieldset']['api_name'], but FieldsetTemplateKickoffSerializer / FieldsetTemplateTaskTemplateSerializer validate each link as { order, api_name }. Saving templates with fieldsets would raise KeyError or skip names even after fixing the pop key.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cff7011. Configure here.

fieldset_fields = FieldTemplate.objects.filter(
fieldset__api_name__in=fieldsets_api_names,
account_id=account.id,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wf name ignores kickoff fieldsets

Medium Severity

_get_raw_fields_from_kickoff only collects fieldset API names from fieldsettemplatekickoff_set entries shaped as fieldset.api_name, and loads fields without scoping to the template being saved. Kickoff fieldsets sent as fieldsets are ignored, so wf_name_template validation can reject valid placeholders or pull fields from another template with the same fieldset api_name.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit cff7011. Configure here.

Comment thread start.sh
echo ""


# 2.2 Stop running containers
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low start.sh:207

set -e at line 4 causes the script to exit immediately when the docker compose command inside the command substitution fails at lines 217 or 219. The non-zero exit status from the command substitution triggers set -e before the if [ $? -eq 0 ] check on line 221 is reached, so the error-handling block (lines 221-226) is dead code. On failure, the user sees silent exit with no error message instead of the intended print_error "$output". The same applies to line 209 where a docker compose down failure also silently kills the script. Consider disabling set -e for these commands or using a different error-handling pattern that doesn't rely on checking $? after a command substitution.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file start.sh around line 207:

`set -e` at line 4 causes the script to exit immediately when the `docker compose` command inside the command substitution fails at lines 217 or 219. The non-zero exit status from the command substitution triggers `set -e` before the `if [ $? -eq 0 ]` check on line 221 is reached, so the error-handling block (lines 221-226) is dead code. On failure, the user sees silent exit with no error message instead of the intended `print_error "$output"`. The same applies to line 209 where a `docker compose down` failure also silently kills the script. Consider disabling `set -e` for these commands or using a different error-handling pattern that doesn't rely on checking `$?` after a command substitution.

Comment thread start.sh
sed -i "s|^#\?\s*POSTGRES_REPLICA_PASSWORD=.*|POSTGRES_REPLICA_PASSWORD=$POSTGRES_PASSWORD|" "$ENV_FILE"
sed -i "s|^#\?\s*REDIS_PASSWORD=.*|REDIS_PASSWORD=$REDIS_PASSWORD|" "$ENV_FILE"
sed -i "s|^#\?\s*RABBITMQ_PASSWORD=.*|RABBITMQ_PASSWORD=$RABBITMQ_PASSWORD|" "$ENV_FILE"
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium start.sh:100

When SERVER_ADDRESS is localhost, the NGINX_CONF_TEMPLATE variable is set to ./nginx/templates/ on line 39 but is never written to .env because the sed command on line 142 is inside the ADDRESS_IS_LOCALHOST = false block. The commented-out value in default.env points to ./nginx/ssl_templates/, so NGINX receives no valid template configuration for localhost deployments.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file start.sh around line 100:

When `SERVER_ADDRESS` is `localhost`, the `NGINX_CONF_TEMPLATE` variable is set to `./nginx/templates/` on line 39 but is never written to `.env` because the `sed` command on line 142 is inside the `ADDRESS_IS_LOCALHOST = false` block. The commented-out value in `default.env` points to `./nginx/ssl_templates/`, so NGINX receives no valid template configuration for localhost deployments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Frontend Web client changes request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants